using System;
using System.Collections.Generic;
using System.Data;

using Elev.Core;

namespace Elev.Core.Generator
{
    public class Grid
    {
        List<Exam> columns;
        List<Student> rows;
        Period period;
        DataTable dt;

        public Period Period { get { return period; } }

        /// <summary>
        /// Checks wether the grid is not empty
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty
        {
            get
            {
                return (dt.Columns.Count == 0);
            }
        }

        /// <summary>
        /// Returns, in order, all the Exam instances of the columns
        /// </summary>
        public Exam[] ColumnHeader
        {
            get
            {
                return columns.ToArray();
            }
        }

        /// <summary>
        /// Returns in order, all the Student instances of the rows
        /// </summary>
        public Student[] RowHeader
        {
            get
            {
                return rows.ToArray();
            }
        }

        /// <summary>
        /// Creates a new instance of a Grid for a given Period
        /// </summary>
        /// <param name="aPeriod"></param>
        public Grid(Period aPeriod)
        {
            period = aPeriod;
            dt = new DataTable();
            columns = new List<Exam>();
            rows = new List<Student>();
        }

        /// <summary>
        /// Adds a new column to the grid.
        /// </summary>
        public void AddColumn(Exam exam)
        {
            dt.Columns.Add(exam.ToString(), typeof(IDimension));
            columns.Add(exam);
        }

        /// <summary>
        /// Adds a new row to the grid.
        /// </summary>
        public void AddRow(Student student, List<Grade> grades)
        {
            if (grades.Count > dt.Columns.Count)
                throw new IndexOutOfRangeException("You cannot have more grades than existing exams");

            DataRow dr = dt.NewRow();

            foreach (Grade grade in grades)
            {
                int colIndex = getExamColumnIndex(grade.ExamID);
                if (colIndex < 0 || colIndex > dt.Columns.Count)
                    throw new IndexOutOfRangeException(string.Format("There is no Exam at column {0}", colIndex));
                
                dr[colIndex] = grade;
            }

            // Now, for each empty cell, create empty Grade instance ...
            for (int i = 0; i < columns.Count; i++)
            {
                if (dr[i].GetType() == typeof(DBNull))
                    dr[i] = new Grade(columns[i].ID, student.ID);
            }

            dt.Rows.Add(dr);
            rows.Add(student);
        }

        public bool HasComment(int RowIndex, int ColIndex)
        {
            if (RowIndex > -1 && ColIndex > -1)
                if (RowIndex < dt.Rows.Count && ColIndex < dt.Columns.Count)
                    if (dt.Rows[RowIndex][ColIndex].GetType() != typeof(System.DBNull))
                        return (((Grade)dt.Rows[RowIndex][ColIndex]).Note != "");

            return false;
        }

        /// <summary>
        /// Returns the instance of the grade for a given student & exam
        /// </summary>
        public Grade GetGrade(int StudentID, int ExamID)
        {
            int rowIndex = getStudentRowIndex(StudentID);
            int colIndex = getExamColumnIndex(ExamID);

            if (rowIndex > -1 && colIndex > -1)
            {
                if (dt.Rows[rowIndex][colIndex].GetType() != typeof(System.DBNull))
                    return (Grade)dt.Rows[rowIndex][colIndex];
            }

            return null;
        }

        /// <summary>
        /// Returns an Array of grades for a given students
        /// </summary>
        public Grade[] GetGradesForStudent(int StudentID)
        {
            // Include all exam types
            return GetGradesForStudent(StudentID, -1);
        }

        /// <summary>
        /// Returns an Array of grades for a given students for a given exam types.
        /// -1 for all types.
        /// </summary>
        public Grade[] GetGradesForStudent(int StudentID, int ExamTypeID)
        {
            int rowIndex = getStudentRowIndex(StudentID);
            List<Grade> grades = new List<Grade>();

            for (int i = 0; i < dt.Columns.Count; i++)
            {
                if (dt.Rows[rowIndex][i].GetType() != typeof(System.DBNull))
                {
                    Grade g = (Grade)dt.Rows[rowIndex][i];
                    if (ExamTypeID == -1 || g.Exam.ExamTypeID == ExamTypeID)
                        grades.Add(g);
                }
            }

            return grades.ToArray();
        }

        /// <summary>
        /// Returns an Array of grades for a given exam
        /// </summary>
        public Grade[] GetGradesForExam(int ExamID)
        {
            int colIndex = getExamColumnIndex(ExamID);
            List<Grade> grades = new List<Grade>();

            for (int i = 0; i < dt.Rows.Count; i++)
            {
                if (dt.Rows[i][colIndex].GetType() != typeof(System.DBNull))
                    grades.Add((Grade)dt.Rows[i][colIndex]);
            }

            return grades.ToArray();
        }

        /// <summary>
        /// Returns the index of the column corresponding to the given Exam
        /// </summary>
        int getExamColumnIndex(int ExamID)
        {
            for (int i = 0; i < columns.Count; i++)
                if (columns[i].ID == ExamID)
                    return i;
            return -1;
        }

        /// <summary>
        /// Returns the index of the row corresponding to the given Student
        /// </summary>
        int getStudentRowIndex(int StudentID)
        {
            for (int i = 0; i < rows.Count; i++)
                if (rows[i].ID == StudentID)
                    return i;
            return -1;
        }
    }
}
